#include "vboGrid.h"

#include <math.h>

#include <GL/glew.h>
#include "extras/utils.h"
#include "image.h"

#include <nvVector.h>

#define BUFFER_OFFSET(i) ((char *)NULL + (i))

typedef struct
{
	float location[4];
	//float tex[2];
	float normal[3];

} Vertex;



VboGrid::VboGrid(int w, int h, float expansionRate){

	this->w = w;
	this->h = h;

	init(expansionRate);

}


void VboGrid::init(float expansionRate){


	_vertices = 0;
	_indices = 0;
	scale = 4.3f;
	float fov = 90.;

	fov /= 360.f;

	// generate indices
	glGenBuffers(1, &_indices);

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indices);
	//glBufferData(GL_ELEMENT_ARRAY_BUFFER, ((w*2)*(h-1))*sizeof(GLuint), 0, GL_STATIC_DRAW);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, ((w*2)+1)*(h-1)*sizeof(GLuint), 0, GL_STATIC_DRAW);

	// fill with indices for rendering mesh as triangle strips
	GLuint *indices = (GLuint *) glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);

	for(int y=0; y<h-1; y++) {
		for(int x=0; x<w-1; x++) {
			*indices++ = y*w+x;
			*indices++ = (y+1)*w+x;
		}
		//*indices++ = y*w; // closed loop
		//*indices++ = (y+1)*w;
		*indices++ = 0xffffffff; // for open loop
	}

	glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

	// generate vertices
	glGenBuffers(1, &_vertices);
	glBindBuffer(GL_ARRAY_BUFFER, _vertices);
	glBufferData(GL_ARRAY_BUFFER, (w*h)*sizeof(Vertex), 0, GL_STATIC_DRAW);


	Vertex *verts = (Vertex *) glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);

	float angle = -fov*M_PI;
	float r = 0;

	// insert vertex values
	for(int y=0; y<h; y++) {
		for(int x=0; x<w; x++) {

			verts->normal[0] = 0.f;
			verts->normal[1] = 1.f;
			verts->normal[2] = 0.f;

			float u = x / (float)(w-1);
			float v = y / (float)(h-1);

			// make a rectangular grid up to a certain distance
			if(r < 2.f){
				verts->location[0]  = (x - 0.5f*w)*.039f;
				//verts->location[2]  = y*.02f;
			}else{
				verts->location[0]  = r*sin(angle);

			}
			verts->location[2]  = r*cos(angle);


			verts->location[1] = 0.0f;
			verts->location[3]  = 1.f;

			//verts->tex[0] = u;
			//verts->tex[1] = v;

			angle =  fov*M_PI * 2.f * x / (float) (w-1) -fov*M_PI;
			verts++;
		}

		if(r > 2.f)
			r += 0.005 * pow(0.01*(double)y + 1.0, 2.0);
		else
			r += 0.02f;

		angle = -fov*M_PI;
	}

	//glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(12));
	glNormalPointer(GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(16));
	glVertexPointer(4, GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(0));

	glUnmapBuffer(GL_ARRAY_BUFFER);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
}

void VboGrid::draw(){
	glEnable(GL_LIGHTING);

	glColor3f(1.0, 1.0, 1.0);

	// Bind our buffers much like we would for texturing

	glBindBuffer(GL_ARRAY_BUFFER, _vertices);
	// Set the state of what we are drawing (I don't think order matters here, but I like to do it in the same
	// order I set the pointers
	//glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glEnableClientState(GL_NORMAL_ARRAY);
	glEnableClientState(GL_VERTEX_ARRAY);

	// Resetup our pointers.  This doesn't reinitialise any data, only how we walk through it
	//glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(12));
	glNormalPointer(GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(16));
	glVertexPointer(4, GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(0));

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,_indices);

	// Actually do our drawing
	glPolygonMode(GL_FRONT_AND_BACK, false ? GL_LINE : GL_FILL);
	//glDrawElements(GL_TRIANGLE_STRIP, ((w*2))*(h-1), GL_UNSIGNED_INT, 0);
	glDrawElements(GL_TRIANGLE_STRIP, ((w*2)+1)*(h-1), GL_UNSIGNED_INT, 0);


	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);


	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	glBindBuffer(GL_ARRAY_BUFFER, 0);

	// Disable our client state back to normal drawing
	//glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glDisableClientState(GL_NORMAL_ARRAY);
	glDisableClientState(GL_VERTEX_ARRAY);



}

VboGrid::~VboGrid(){

	if(_vertices)
		glDeleteBuffers(1, &_vertices);
	_vertices = 0;

	if(_indices)
		glDeleteBuffers(1, &_indices);
	_indices = 0;

}

